﻿// Inner Fire 游戏引擎库
// DxIniter 渲染器初始化功能
//
// Copyright (c) 2025 by 尔西大帝. All rights reserved.
//
// 用于DirectX的渲染器。
// 此文件分装用于初始化功能
//
// Author: 尔西大帝
// Email: 2670613871@qq.com
// Created: 2025-04-23

#include "dx_initer.h"
#include "dx_graphics.h"
#include <dx/core/dx_exception.h>

namespace ifire::dx {
DxIniter::DxIniter(DxGraphics* dx) : DxModule(dx) {}


void DxIniter::CreateDevice() {
  // 得到工厂
  HASSERT(CreateDXGIFactory1(IID_PPV_ARGS(&factory_)));

  // 尝试创建硬件设备
  HRESULT hardware_result =
      D3D12CreateDevice(nullptr, // nullptr = 使用默认适配器
          D3D_FEATURE_LEVEL_11_0, // 最低的支持，如果达不到这个要求，则失败
          IID_PPV_ARGS(&device_));

  // 如果失败，则变为WARP设备
  if (FAILED(hardware_result)) {
    HASSERT(factory_->EnumWarpAdapter(IID_PPV_ARGS(&warp_adapter_)));
    HASSERT(D3D12CreateDevice(
        warp_adapter_.Get(), D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&device_)));
  }

  // 创建Fence
  HASSERT(
      device_->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&fence_)));
}

void DxIniter::CreateCommandObjects() {
  // 创建命令队列（使用Direct模式）
  D3D12_COMMAND_QUEUE_DESC queue_desc = {};
  queue_desc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
  queue_desc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
  HASSERT(device_->CreateCommandQueue(&queue_desc, //
      IID_PPV_ARGS(&cmd_queue_)));

  // 创建命令分配器
  HASSERT(device_->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT,
      IID_PPV_ARGS(cmd_allocator_.GetAddressOf())));

  // 创建命令列表
  HASSERT(device_->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT,
      cmd_allocator_.Get(), nullptr, IID_PPV_ARGS(cmd_list_.GetAddressOf())));

  // 要把命令列表置于关闭状态。因为第一次引用命令列表时，需要对其重置。
  // 调用重置方法之前需要将其关闭。
  cmd_list_->Close();
}

void DxIniter::FlushCommandQueue() {
  // 步进fence点，使当前的命令节点为此fence.
  current_fence_++;
  // 向命令队列中添加一条指令，设置一个新的栅栏点。在GPU时间线上，
  // 新的栅栏点要等到GPU完成处理所有在此Signal()之前的命令后才会设置。
  HASSERT(GetCmdQueue()->Signal(fence_.Get(), current_fence_));

  // 等待GPU执行到此fence点
  if (fence_->GetCompletedValue() < current_fence_) {
    // 创建一个匿名事件
    HANDLE handle = CreateEventEx(nullptr, NULL, false, EVENT_ALL_ACCESS);
    // 设置当GPU完成到fence点的时候执行事件
    HASSERT(fence_->SetEventOnCompletion(current_fence_, handle));
    // 等待事件触发
    WaitForSingleObject(handle, INFINITE);
    // 关闭事件
    CloseHandle(handle);
  }
}

void DxIniter::NextFence() {
  // 步进当前帧资源的围栏
  // 向命令队列添加一条指令以设置一个新的栅栏点。因为我们处于GPU时间线上，
  // 新的栅栏点要等到GPU处理完所有在此Signal()之前的命令后才会设置。
  dx_->CurrentFrameRes()->Fence = ++current_fence_;
  cmd_queue_->Signal(fence_.Get(), current_fence_);
}
} // namespace ifire::dx